package com.ciei.dpagm.service;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ciei.dpagm.common.enums.ErrorCodeEnum;
import com.ciei.dpagm.common.enums.FileRelationTypeEnum;
import com.ciei.dpagm.common.enums.IsNotEnum;
import com.ciei.dpagm.common.service.BaseService;
import com.ciei.dpagm.entity.*;
import com.ciei.dpagm.mapper.DriverLibraryDirectoryMapper;
import com.ciei.dpagm.util.APP;
import com.ciei.dpagm.util.CompressUtils;
import com.ciei.dpagm.util.FileUtils;
import com.ciei.dpagm.util.JsonUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletResponse;
import java.sql.Timestamp;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author wangyan
 * @date 2023/05/08
 */
@Service
public class DriverLibraryDirectoryService extends BaseService<DriverLibraryDirectoryMapper, DriverLibraryDirectory> {

    @Value("${baseFilePath}")
    private String baseFilePath;

    @Autowired
    private FilesService filesService;

    @Autowired
    private DriverLibraryConfigService driverLibraryConfigService;

    /**
     * 加载驱动库目录树
     * @return
     */
    public JSONObject loadTree(JSONObject query){
        Integer driverLibraryId = query.getInteger("driverLibraryId");
        List<DriverLibraryDirectory> directoryList = findByProperty(DriverLibraryDirectory::getDriverLibraryId,driverLibraryId);
        List<Integer> fileDirectoryIds = directoryList.stream().filter(e ->e.getIsFile().equals(IsNotEnum.IS.getId())).map(DriverLibraryDirectory::getDirectoryId).collect(Collectors.toList());
        List<Files> directoryFilesList = filesService.getFileList(FileRelationTypeEnum.DRIVER_PROJECT.getRelationTypeId(),fileDirectoryIds);
        DriverLibraryDirectoryVo directoryVoList = buildDirectoryVoTree(directoryList,directoryFilesList);
        return JsonUtil.getSuccess("").fluentPut(APP.DATA, directoryVoList);
    }

    /**
     * 构建DriverLibraryDirectoryVo驱动库目录树形结构数据
     * @param dataList
     * @param directoryFilesList
     * @return
     */
    public DriverLibraryDirectoryVo buildDirectoryVoTree(List<DriverLibraryDirectory> dataList,List<Files> directoryFilesList) {
        List<DriverLibraryDirectoryVo> nodes = new ArrayList<>();
        dataList.forEach(data -> {
            Files files = null;
            if (IsNotEnum.IS.getId().equals(data.getIsFile())){
                files = directoryFilesList.stream().filter(file -> file.getRelationId().equals(data.getDirectoryId())).findFirst().orElse(null);
            }
            DriverLibraryDirectoryVo node = new DriverLibraryDirectoryVo(data.getDirectoryId(),data.getParentDirectoryId(),data.getDirectoryName(),data.getIsFile(),
                    files == null ? "" : files.getPath());
            nodes.add(node);
        });
        return toDirectoryVoTree(nodes);
    }

    /**
     * 构建DriverLibraryDirectoryVo树形结构数据
     * @param nodes     总数据集
     * @return          树形结构数据集
     */
    public DriverLibraryDirectoryVo toDirectoryVoTree(List<DriverLibraryDirectoryVo> nodes) {
        Map<Integer, List<DriverLibraryDirectoryVo>> childrenMap = nodes.stream()
                .filter(node -> node.getParentDirectoryId() != 0)
                .collect(Collectors.groupingBy(DriverLibraryDirectoryVo::getParentDirectoryId));
        nodes.forEach(node -> {
            List<DriverLibraryDirectoryVo> children = childrenMap.get(node.getDirectoryId());
            if (children != null) {
                node.setChildren(children);
            }
        });
        return nodes.stream().filter(node -> node.getParentDirectoryId() == 0).findFirst().orElse(null);
    }

    /**
     * 创建目录
     * @param parentDirectoryId
     * @param directoryName
     * @param isFile
     * @param driverLibraryId
     * @return
     */
    public Integer createDirectory(Integer parentDirectoryId,String directoryName,Integer isFile,Integer driverLibraryId){
        DriverLibraryDirectory directory = new DriverLibraryDirectory();
        directory.setParentDirectoryId(parentDirectoryId);
        directory.setDirectoryName(directoryName);
        directory.setIsFile(isFile);
        directory.setDriverLibraryId(driverLibraryId);
        save(directory);
        return directory.getDirectoryId();
    }

    /**
     * 编辑目录名称
     * @param data
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public JSONObject editDirectoryName(JSONObject data){
        Integer directoryId = data.getInteger("directoryId");
        String directoryName = data.getString("directoryName");
        DriverLibraryDirectory directory = findById(directoryId);
        if (directory == null){
            return JsonUtil.getFail(ErrorCodeEnum.DATA_NOT_FOUND,"找不到对应的文件数据!");
        }
        if (isDuplicateByName(directory.getParentDirectoryId(),directoryId,directoryName)){
            return JsonUtil.getFail(ErrorCodeEnum.DUPLICATE_DATA,"同级目录下文件名称已存在!");
        }
        if (directory.getIsFile().equals(IsNotEnum.IS.getId())){
            Files file = filesService.getFileList(FileRelationTypeEnum.DRIVER_PROJECT.getRelationTypeId(),directoryId).get(0);
            filesService.editName(file.getFileId(),directoryName);
        }
        directory.setDirectoryName(directoryName);
        updateById(directory);
        return JsonUtil.getSuccess("");
    }

    /**
     * 判断目录名称是否重复
     * @param parentDirectoryId
     * @param directoryId
     * @param directoryName
     * @return
     */
    public Boolean isDuplicateByName(Integer parentDirectoryId,Integer directoryId,String directoryName){
        if (parentDirectoryId == 0){
            return false;
        }
        QueryWrapper<DriverLibraryDirectory> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(DriverLibraryDirectory::getParentDirectoryId,parentDirectoryId).eq(DriverLibraryDirectory::getDirectoryName,directoryName);
        if (directoryId != null){
            queryWrapper.lambda().ne(DriverLibraryDirectory::getDirectoryId,directoryId);
        }
        return count(queryWrapper) > 0;
    }

    /**
     * 在相同文件名称后面追加(i)
     * @param fileName
     * @param index
     * @return
     */
    public String addIndexToFileName(String fileName, int index) {
        int dotIndex = fileName.lastIndexOf(".");
        if (dotIndex == -1) {
            return fileName + "(" + index + ")";
        } else {
            String name = fileName.substring(0, dotIndex);
            String ext = fileName.substring(dotIndex);
            return name + "(" + index + ")" + ext;
        }
    }

    /**
     * 获取文件名称
     * @param nameList
     * @param name
     * @return
     */
    public String getFileName(List<String> nameList,String name) {
        int index = 1;
        String newFileName = name;
        while (nameList.contains(newFileName)) {
            newFileName = addIndexToFileName(name, index++);
        }
        return newFileName;
    }

    /**
     * 查询当前目录及所有子目录集合
     * @param directoryList
     * @param directory
     * @return
     */
    public List<Map<String,Object>> oneLevelDirectory(List<DriverLibraryDirectory> directoryList,DriverLibraryDirectory directory){
        List<Map<String,Object>> directoryMapList = new ArrayList<>();
        int level = 0;
        directoryMapList.add(generateDirectoryMap(directory,level));
        findChildDirectory(directoryList, directory.getDirectoryId(), directoryMapList,level+1);
        return directoryMapList;
    }

    /**
     * 查找所有符合条件的子目录
     * @param directoryList
     * @param parentId
     * @param directoryMapList
     * @param i
     */
    private void findChildDirectory(List<DriverLibraryDirectory> directoryList, Integer parentId, List<Map<String,Object>> directoryMapList,int i) {
        for (DriverLibraryDirectory directory : directoryList) {
            if (directory.getParentDirectoryId().equals(parentId)) {
                directoryMapList.add(generateDirectoryMap(directory,i));
                findChildDirectory(directoryList, directory.getDirectoryId(), directoryMapList,i+1);
            }
        }
    }

    /**
     * 查询父目录及所有子目录ID集合
     * @param directoryList
     * @param parentId
     * @return
     */
    public List<Integer> getDirectoryIds(List<DriverLibraryDirectory> directoryList, Integer parentId) {
        List<Integer> ids = new ArrayList<>();
        ids.add(parentId);
        List<DriverLibraryDirectory> children = directoryList.stream()
                .filter(d -> d.getParentDirectoryId().equals(parentId))
                .collect(Collectors.toList());
        children.forEach(child -> ids.addAll(getDirectoryIds(directoryList, child.getDirectoryId())));
        return ids;
    }

    /**
     * 查询指定子目录及其所有父目录的名称,中间以"/"分割
     * @param directoryId
     * @return
     */
    public String getParentDirectoryNames(List<Map<String,Object>> dataList,Integer directoryId) {
        List<String> parentDirectoryNames = new ArrayList<>();
        Map<String,Object> directory = dataList.stream().filter(e ->e.get("directory_id").equals(directoryId)).findFirst().orElse(null);
        if (directory != null) {
            parentDirectoryNames.add(directory.get("directory_name").toString());
            Integer parentDirectoryId = (Integer) directory.get("parent_directory_id");
            Map<String,Object> parentDirectory;
            while (parentDirectoryId != 0) {
                Integer finalParentDirectoryId = parentDirectoryId;
                parentDirectory = dataList.stream().filter(e ->e.get("directory_id").equals(finalParentDirectoryId)).findFirst().orElse(null);
                if (parentDirectory != null) {
                    parentDirectoryNames.add(parentDirectory.get("directory_name").toString());
                    parentDirectoryId = (Integer) parentDirectory.get("parent_directory_id");
                } else {
                    parentDirectoryId = 0;
                }
            }
        }
        Collections.reverse(parentDirectoryNames);
        return StringUtils.join(parentDirectoryNames,"/");
    }

    /**
     * 删除整个驱动工程目录
     * @param driverLibraryId
     */
    public void deleteByDriverLibraryId(Integer driverLibraryId){
        List<DriverLibraryConfig> configList = driverLibraryConfigService.findByProperty(DriverLibraryConfig::getDriverLibraryId,driverLibraryId);
        if (!configList.isEmpty()){
            driverLibraryConfigService.removeByIds(configList.stream().map(DriverLibraryConfig::getConfigId).collect(Collectors.toList()));
        }
        List<DriverLibraryDirectory> directoryList = findByProperty(DriverLibraryDirectory::getDriverLibraryId,driverLibraryId);
        //删除工程目录数据
        removeByIds(directoryList.stream().map(DriverLibraryDirectory::getDirectoryId).collect(Collectors.toList()));
        //删除工程目录文件
        List<Integer> fileDirectoryIds = directoryList.stream().filter(e ->IsNotEnum.IS.getId().equals(e.getIsFile())).map(DriverLibraryDirectory::getDirectoryId).collect(Collectors.toList());
        filesService.deleteFiles(FileRelationTypeEnum.DRIVER_PROJECT.getRelationTypeId(),fileDirectoryIds);
    }

    /**
     * 删除指定层级工程目录
     * @param data
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public JSONObject delete(JSONObject data){
        Integer directoryId = data.getInteger("directoryId");
        DriverLibraryDirectory directory = findById(directoryId);
        if (directory != null){
            //List<Map<String,Object>> directoryMapList = getBaseMapper().oneLevelDirectory(directoryId);
            List<Integer> directoryIds = Arrays.asList(directoryId);
            List<Integer> fileDirectoryIds = Arrays.asList(directoryId);
            if (IsNotEnum.NOT.getId().equals(directory.getIsFile())){
                List<DriverLibraryDirectory> directoryList = findByProperty(DriverLibraryDirectory::getDriverLibraryId,directory.getDriverLibraryId());
                List<Map<String,Object>> directoryMapList = oneLevelDirectory(directoryList,directory);
                directoryIds = directoryMapList.stream().map(e ->(Integer)e.get("directory_id")).collect(Collectors.toList());
                fileDirectoryIds = directoryMapList.stream().filter(e ->Objects.equals(e.get("is_file"),true)).map(e ->(Integer)e.get("directory_id")).collect(Collectors.toList());
            }
            List<DriverLibraryConfig> configList = driverLibraryConfigService.findByProperty(DriverLibraryConfig::getDriverLibraryId,directory.getDriverLibraryId());
            List<Integer> deleteConfigIds = new ArrayList<>();
            for (DriverLibraryConfig driverLibraryConfig : configList){
                if (directoryIds.contains(driverLibraryConfig.getDirectoryId())){
                    deleteConfigIds.add(driverLibraryConfig.getConfigId());
                }
            }
            //删除工程配置数据
            if (!deleteConfigIds.isEmpty()){
                driverLibraryConfigService.removeByIds(deleteConfigIds);
            }
            //删除工程目录数据
            removeByIds(directoryIds);
            //删除工程目录文件
            filesService.deleteFiles(FileRelationTypeEnum.DRIVER_PROJECT.getRelationTypeId(),fileDirectoryIds);
        }
        return JsonUtil.getSuccess("");
    }


    /**
     * 生成目录map(层级)
     * @param directory
     * @param level
     * @return
     */
    private Map<String,Object> generateDirectoryMap(DriverLibraryDirectory directory,Integer level){
        Map<String,Object> directoryMap = new HashMap<>();
        directoryMap.put("level",level);
        directoryMap.put("directory_id",directory.getDirectoryId());
        directoryMap.put("parent_directory_id",directory.getParentDirectoryId());
        directoryMap.put("directory_name",directory.getDirectoryName());
        directoryMap.put("is_file",IsNotEnum.IS.getId().equals(directory.getIsFile()));
        return directoryMap;
    }


    /**
     * 下载文件
     * @param data
     * @param response
     * @return
     */
    public JSONObject download(JSONObject data, HttpServletResponse response){
        Integer directoryId = data.getInteger("directoryId");
        DriverLibraryDirectory directory = findById(directoryId);
        if (directory == null){
            return JsonUtil.getFail(ErrorCodeEnum.DATA_NOT_FOUND,"找不到对应的工程目录数据!");
        }
        //List<Map<String,Object>> directoryMapList = getBaseMapper().oneLevelDirectory(directoryId);
        List<DriverLibraryDirectory> directoryList = findByProperty(DriverLibraryDirectory::getDriverLibraryId,directory.getDriverLibraryId());
        List<Map<String,Object>> directoryMapList = oneLevelDirectory(directoryList,directory);
        List<Integer> fileDirectoryIds = directoryMapList.stream().filter(e -> Objects.equals(e.get("is_file"),true)).map(e ->(Integer)e.get("directory_id")).collect(Collectors.toList());
        List<Files> filesList = filesService.getFileList(FileRelationTypeEnum.DRIVER_PROJECT.getRelationTypeId(),fileDirectoryIds);
        String nanoTime = String.valueOf(System.nanoTime());
        //待下载文件夹临时路径
        String targetTempFilePath = baseFilePath + "files/" + "temp/" + nanoTime + "/";
        copyFilesToDirectoryTree(targetTempFilePath,filesList,directoryMapList);

        String sourceFolderPath = targetTempFilePath + directory.getDirectoryName();
        CompressUtils.createCompress(response,sourceFolderPath,targetTempFilePath,directory.getDirectoryName());
        return JsonUtil.getSuccess("");
    }

    /**
     * 复制文件到指定目录树中
     * @param targetTempFilePath
     * @param filesList
     * @param directoryMapList
     */
    public void copyFilesToDirectoryTree(String targetTempFilePath,List<Files> filesList,List<Map<String,Object>> directoryMapList){
        Map<String,List<Map<String,Object>>> groupByLevelMap = directoryMapList.stream().collect(Collectors.groupingBy(e->e.get("level").toString()));
        groupByLevelMap.forEach((key,value)->{
            for (Map<String,Object> map : value){
                Integer currentDirectoryId = (Integer) map.get("directory_id");
                String targetFilePath = targetTempFilePath + getParentDirectoryNames(directoryMapList, currentDirectoryId);
                if (Objects.equals(map.get("is_file"),false)){
                    FileUtils.checkDirs(targetFilePath);
                }else {
                    Files files = filesList.stream().filter(e ->e.getRelationId().equals(currentDirectoryId)).findFirst().orElse(null);
                    if (files != null){
                        String sourcePath = baseFilePath + files.getPath().replaceFirst("uploads/", "");
                        FileUtils.copyFile(sourcePath,targetFilePath);
                    }
                }
            }
        });
    }

    /**
     * 上传文件
     * @param data
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public JSONObject uploadFile(JSONObject data, Users currentUser){
        JSONArray directoryFiles = data.getJSONArray("directoryFiles");
        Integer parentDirectoryId = data.getInteger("parentDirectoryId");
        if (directoryFiles == null || directoryFiles.isEmpty() || parentDirectoryId == null){
            return JsonUtil.getFail(ErrorCodeEnum.INVALID_PARAM,"缺少参数,请检查!");
        }
        DriverLibraryDirectory parentDirectory = findById(parentDirectoryId);
        if (parentDirectory == null){
            return JsonUtil.getFail(ErrorCodeEnum.DATA_NOT_FOUND,"找不到对应的工程目录数据!");
        }
        String directoryName = directoryFiles.getJSONObject(0).getString("fileName");
        List<DriverLibraryDirectory> directoryList = findByProperty(DriverLibraryDirectory::getParentDirectoryId,parentDirectoryId);
        if (!directoryList.isEmpty()){
            List<String> directoryNameList = directoryList.stream().map(DriverLibraryDirectory::getDirectoryName).collect(Collectors.toList());
            //如果文件名称重复，在文件名称后面+(i)
            directoryName = getFileName(directoryNameList,directoryName);
        }
        Integer directoryId = createDirectory(parentDirectoryId,directoryName,IsNotEnum.IS.getId(),parentDirectory.getDriverLibraryId());
        filesService.saveFiles(directoryFiles,currentUser.getUserId(),FileRelationTypeEnum.DRIVER_PROJECT.getRelationTypeId(),directoryId);
        return JsonUtil.getSuccess("").fluentPut(APP.DATA,directoryId);
    }

    /**
     * 上传文件夹
     * @param data
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public JSONObject uploadFolder(JSONObject data, Users currentUser){
        JSONArray directoryFiles = data.getJSONArray("directoryFiles");
        Integer parentDirectoryId = data.getInteger("parentDirectoryId");
        if (directoryFiles == null || directoryFiles.isEmpty() || parentDirectoryId == null){
            return JsonUtil.getFail(ErrorCodeEnum.INVALID_PARAM,"缺少参数,请检查!");
        }
        DriverLibraryDirectory parentDirectory = findById(parentDirectoryId);
        if (parentDirectory == null){
            return JsonUtil.getFail(ErrorCodeEnum.DATA_NOT_FOUND,"找不到对应的工程目录数据!");
        }
        List<Files> folderFiles = directoryFiles.toJavaList(Files.class);
        List<String> paths = folderFiles.stream().map(Files::getFileName).collect(Collectors.toList());
        DriverLibraryDirectoryBo directoryBo = buildDirectoryBoTree(paths);
        String folderName = directoryBo.getChildren().get(0).getDirectoryName();
        List<DriverLibraryDirectory> directoryList = findByProperty(DriverLibraryDirectory::getParentDirectoryId,parentDirectoryId);
        if (!directoryList.isEmpty()){
            List<String> directoryNameList = directoryList.stream().map(DriverLibraryDirectory::getDirectoryName).collect(Collectors.toList());
            //如果第一层文件夹名称重复，在文件夹后面+(i)
            folderName = getFileName(directoryNameList,folderName);
            directoryBo.getChildren().get(0).setDirectoryName(folderName);
        }
        saveDirectoryLoop(parentDirectory.getDriverLibraryId(),parentDirectoryId,directoryBo.getChildren(),folderFiles);

        Timestamp timestamp = new Timestamp(System.currentTimeMillis());
        Integer userId = currentUser.getUserId();
        folderFiles.forEach(e ->{
            e.setRelationTypeId(FileRelationTypeEnum.DRIVER_PROJECT.getRelationTypeId());
            e.setFileName(e.getFileName().substring(e.getFileName().lastIndexOf("/") + 1));
            e.setUploadTime(timestamp);
            e.setUploadUserId(userId);
        });
        filesService.saveBatch(folderFiles);
        DriverLibraryDirectory directory = findOneByProperties(new String[]{"parentDirectoryId","directoryName"},new Object[]{parentDirectoryId,folderName});
        return JsonUtil.getSuccess("").fluentPut(APP.DATA,directory == null ? "" : directory.getDirectoryId());
    }

    /**
     * 构建目录结构树
     * @param paths
     * @return
     */
    public DriverLibraryDirectoryBo buildDirectoryBoTree(List<String> paths) {
        DriverLibraryDirectoryBo root = new DriverLibraryDirectoryBo();
        for (String path : paths) {
            String[] parts = path.split("/");
            DriverLibraryDirectoryBo node = root;
            for (int i = 0; i < parts.length; i++) {
                String part = parts[i];
                if (part.isEmpty()) {
                    continue;
                }
                DriverLibraryDirectoryBo child = findChild(node, part);
                if (child == null) {
                    child = new DriverLibraryDirectoryBo();
                    child.setDirectoryName(part);
                    child.setIsFile(i == parts.length - 1 ? IsNotEnum.IS.getId() : IsNotEnum.NOT.getId());
                    child.setFilePath(path);
                    node.getChildren().add(child);
                }
                node = child;
            }
        }
        return root;
    }

    /**
     * 查找子节点列表中是否已经存在指定名称的子节点
     * @param node
     * @param name
     * @return
     */
    public DriverLibraryDirectoryBo findChild(DriverLibraryDirectoryBo node, String name) {
        for (DriverLibraryDirectoryBo child : node.getChildren()) {
            if (child.getDirectoryName().equals(name)) {
                return child;
            }
        }
        return null;
    }

    /**
     * 保存目录数据
     * @param driverLibraryId
     * @param parentDirectoryId
     * @param directoryBoList
     */
    public void saveDirectoryLoop(Integer driverLibraryId,Integer parentDirectoryId,List<DriverLibraryDirectoryBo> directoryBoList,List<Files> directoryFileList){
        for (DriverLibraryDirectoryBo directoryBo : directoryBoList){
            DriverLibraryDirectory directory = new DriverLibraryDirectory(parentDirectoryId,directoryBo.getDirectoryName(),directoryBo.getIsFile(),driverLibraryId);
            save(directory);
            Integer directoryId = directory.getDirectoryId();
            directoryBo.setDirectoryId(directoryId);
            directoryFileList.forEach(e ->{
                if (e.getFileName().equals(directoryBo.getFilePath())){
                    e.setRelationId(directoryId);
                }
            });
            if (!directoryBo.getChildren().isEmpty()){
                saveDirectoryLoop(driverLibraryId,directoryId,directoryBo.getChildren(),directoryFileList);
            }
        }
    }

    /**
     * 添加文件夹
     * @param data
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public JSONObject addFolder(JSONObject data){
        Integer parentDirectoryId = data.getInteger("parentDirectoryId");
        String directoryName = data.getString("directoryName");
        if (parentDirectoryId == null || StringUtils.isBlank(directoryName)){
            return JsonUtil.getFail(ErrorCodeEnum.INVALID_PARAM,"缺少参数，请检查!");
        }
        DriverLibraryDirectory parentDirectory = findById(parentDirectoryId);
        if (parentDirectory == null){
            return JsonUtil.getFail(ErrorCodeEnum.DATA_NOT_FOUND,"找不到对应的父级目录数据!");
        }
        if (isDuplicateByName(parentDirectoryId,null,directoryName)){
            return JsonUtil.getFail(ErrorCodeEnum.DUPLICATE_DATA,"同级目录下文件夹名称已存在!");
        }
        Integer directoryId = createDirectory(parentDirectoryId,directoryName,IsNotEnum.NOT.getId(),parentDirectory.getDriverLibraryId());
        return JsonUtil.getSuccess("").fluentPut(APP.DATA,directoryId);
    }

}
